import numpy as np
import PIL.Image
import mdp
import math
import sys

class PCAImageCompressor:

	def __init__(self, width, height, block_size=10, outputdim=None):
		self._width = width
		self._height = height
		self._block_size = block_size
		self._outputdim = outputdim
		
		self._pcan = mdp.nodes.PCANode(output_dim=outputdim)
		
		self._wb = width/block_size
		self._hb = height/block_size
		self._nih = self._hb * self._wb
		self._niw = block_size * block_size
		
	@property
	def width(self):
		return self._width
		
	@width.setter
	def width(self, w):
		self._width = w
		self._wb = w/self._block_size
		self._nih = self._hb * self._wb
		self._pcan = mdp.nodes.PCANode(output_dim = self._outputdim)
		
	@property
	def height(self):
		return self._height
		
	@height.setter
	def height(self, h):
		self._height = h
		self._hb = h/self._block_size
		self._nih = self._hb * self._wb
		self._pcan = mdp.nodes.PCANode(output_dim = self._outputdim)
		
	@property
	def block_size(self):
		return self._block_size
		
	@block_size.setter
	def block_size(self, bs):
		self._block_size = bs
		self._wb = self._width/bs
		self._hb = self._height/bs
		self._nih = self._hb * self._wb
		self._niw = bs * bs
		self._pcan = mdp.nodes.PCANode(output_dim = self._outputdim)
		
	@property
	def outputdim(self):
		return self._outputdim
		
	@outputdim.setter
	def outputdim(self, od):
		self._outputdim = od
		self._pcan = mdp.nodes.PCANode(output_dim = od)
		
	@property
	def recmatrix(self):
		return self._pcan.get_recmatrix()
		
	@property
	def avg(self):
		return self._pcan.avg
		
	#recibe una imagen y la transforma generando una matriz de nxp donde
	#n es la cantidad de bloques y p la cantidad de elementos por bloque
	def _transform_image(self, image):
		grayscale_image = image.convert('L')
		image_array = np.array(grayscale_image)

		new_image_list = [[0 for i in range(self._niw)] for j in range(self._nih)]
		
		block = 0
		elem = 0
		for i in range(self._hb):
			for j in range(self._wb):
				for k in range(self._block_size):
					for l in range(self._block_size):
						f = i * self._block_size + k
						c = j * self._block_size + l
						new_image_list[block][elem] = image_array[f][c]
						elem += 1
				elem = 0
				block += 1

		return np.array(new_image_list)
		
	def convert_matrix_to_row(self, matrix):
		row = []
		
		for f in range(len(matrix)):
			for c in range(len(matrix[0])):
				row.append(matrix[f][c])
				
		return row
		
	def compress_image(self, image):
		return self._pcan.execute(self._transform_image(image).astype('float'))
		
	def reconstruct_image(self, pcar, recmatrix, avg):
		rec_array = np.uint8(pcar.dot(recmatrix) + avg)

		#organizo la imagen de nuevo
		ch = self._hb * self._block_size
		cw = self._wb * self._block_size
		compressed = [[0 for i in range(cw)] for j in range(ch)]

		for b in range(self._nih):
			for e in range(self._niw):
				f = (b/self._wb)*self._block_size + e/self._block_size
				c = (b%self._wb)*self._block_size + e%self._block_size
				compressed[f][c] = rec_array[b][e]

		return PIL.Image.fromarray(np.uint8(np.array(compressed)))

